
package br.com.aeternu.controleatendimento.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import javax.xml.bind.PropertyException;
import org.apache.log4j.Logger;

public final class PropertyUtil
{

   public static final String PRP_LOCAL_BASE = "local.base";
   public static final String PRP_UNIDADE = "unidade";
   private static Logger logger = Logger.getLogger(PropertyUtil.class);
   private static Properties privateProperties;
   private static File privatePropertyFile;
   private static String privatePropertyPath;
   private static Map<Category, Map<String, String>> propertiesCategorized;
   private static Properties publicProperties;
   private static File publicPropertyFile;
   private static String publicPropertyPath;

   static
   {
      privateProperties = new Properties();
      publicProperties = new Properties();

      privatePropertyPath = System.getProperty("user.home") + File.separator + ".controleatendimento";
      publicPropertyPath = "./";

      privatePropertyFile = new File(privatePropertyPath + File.separator + "controleatendimento.properties");
      publicPropertyFile = new File(publicPropertyPath + File.separator + "controleatendimento.properties");

      // Carrega as propriedades padrão
      propertiesCategorized = new ConcurrentHashMap<Category, Map<String, String>>();
      loadDefaultProperties();

      try
      {
         // Cria diretórios
         File privateDir = new File(privatePropertyPath);
         File publicDir = new File(publicPropertyPath);

         if ((!(privateDir.exists())) && (!(privateDir.mkdirs())))
         {
            throw new PropertyException("Nao foi possível criar o diretório: " + privateDir.getAbsolutePath());
         }

         if ((!(publicDir.exists())) && (!(publicDir.mkdirs())))
         {
            throw new PropertyException("Nao foi possível criar o diretório: " + publicDir.getAbsolutePath());
         }

         // Cria os arquivos de propriedade, caso não existam, com as propriedades padrão
         if (!(privatePropertyFile.exists()))
         {
            privatePropertyFile.createNewFile();
            loadDefaultProperties(Category.PRIVATE);
         }

         if (!(publicPropertyFile.exists()))
         {
            publicPropertyFile.createNewFile();
            loadDefaultProperties(Category.PUBLIC);
         }

         // Converte os arquivos em propriedades
         FileInputStream privateFIS = new FileInputStream(privatePropertyFile);
         FileInputStream publicFIS = new FileInputStream(publicPropertyFile);

         privateProperties.load(privateFIS);
         publicProperties.load(publicFIS);

         privateFIS.close();
         publicFIS.close();

         validateProperties();
      }
      catch (PropertyException e)
      {
         logger.error(e.getMessage(), e);
      }
      catch (IOException e)
      {
         logger.error(e.getMessage(), e);
      }
   };

   /**
    * Cria uma nova instância do tipo PropertyUtil.
    * <P>
    */
   private PropertyUtil()
   {
   }

   public static Properties getPrivateProperties()
   {
      return Category.PRIVATE.getProperties();
   }

   public static Properties getPublicProperties()
   {
      return Category.PUBLIC.getProperties();
   }

   /**
    * Obtem o conteúdo de uma propriedade específica.
    * 
    * @param chave
    * @return o valor da propriedade.
    */
   public static String getProperty(String key)
   {
      String valor = "";
      key = key.toLowerCase();

      for (Category category : Category.values())
      {
         Properties properties = category.getProperties();

         if (properties.containsKey(key))
            valor = String.valueOf(properties.get(key));
      }

      return valor;
   }

   public static String getVersao()
   {
      String propFileName = "versao.properties";
      Properties properties = new Properties();
      String version = "";

      try
      {
         InputStream inStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(propFileName);

         if (inStream != null)
         {
            properties.load(inStream);
            version = properties.getProperty("versao", "");
            inStream.close();
         }
      }
      catch (IOException e)
      {
      }

      return version;
   }

   public static void setProperty(Map<String, String> keyValue) throws IOException
   {
      for (Category category : Category.values())
      {
         switch (category)
         {
            case PUBLIC:
               if (keyValue.containsKey(PRP_LOCAL_BASE))
                  setProperty(category, PRP_LOCAL_BASE, keyValue.get(PRP_LOCAL_BASE));
               if (keyValue.containsKey(PRP_UNIDADE))
                  setProperty(category, PRP_UNIDADE, keyValue.get(PRP_UNIDADE));
               break;
            default:
         }
      }
   }

   private static void loadDefaultProperties()
   {
      for (Category category : Category.values())
      {
         Map<String, String> keyValue = new HashMap<String, String>();

         switch (category)
         {
            case PUBLIC:
               keyValue.put(PRP_LOCAL_BASE, "./ControleAtendimento.xls");
               keyValue.put(PRP_UNIDADE, "");
               break;
            default:
         }

         propertiesCategorized.put(category, keyValue);
      }
   }

   /**
    * Carrega valores padrão de propriedades privadas para inicialização do sistema.
    * 
    * @throws IOException
    */
   private static void loadDefaultProperties(Category category) throws IOException
   {
      Properties properties = category.getProperties();

      Map<String, String> keyValue = propertiesCategorized.get(category);
      for (String key : keyValue.keySet())
      {
         properties.put(key, keyValue.get(key));
      }

      writeProperties(category, properties);
   }

   private static void setProperty(Category category, String key, String value) throws IOException
   {
      Properties properties = category.getProperties();
      properties.put(key, value);
      writeProperties(category, properties);
   }

   private static void validateProperties() throws IOException
   {
      for (Category category : Category.values())
      {
         Map<String, String> keyValue = propertiesCategorized.get(category);
         Iterator<Map.Entry<String, String>> iter = keyValue.entrySet().iterator();

         while (iter.hasNext())
         {
            Map.Entry<String, String> entry = iter.next();
            String key = entry.getKey();

            if (!category.getProperties().containsKey(key))
            {
               logger.error("Propriedade ausente: " + key);
               setProperty(category, key, entry.getValue());
            }
         }
      }
   }

   private static void writeProperties(Category category, Properties properties) throws IOException
   {
      FileWriter configFileWriter = new FileWriter(category.getPropertyFile());
      properties.store(configFileWriter, "");
      configFileWriter.close();

      logger.debug("Arquivo de propriedades gravado com sucesso");
   }

   private enum Category
   {
      PRIVATE(privateProperties, privatePropertyFile), PUBLIC(publicProperties, publicPropertyFile);

      private Properties properties;
      private File propertyFile;

      Category(Properties properties, File propertyFile)
      {
         this.properties = properties;
         this.propertyFile = propertyFile;
      }

      public Properties getProperties()
      {
         return properties;
      }

      public File getPropertyFile()
      {
         return propertyFile;
      }
   }
}
